/*:
 
 Swift 对于结构化的编写异步和并行代码有着原生的支持。
 
 **并发：** 包含异步和并行两层含义
 
 **异步：** 异步代码可以被挂起并在之后继续执行，同一时间只能有一段代码被执行。代码支持挂起和继续执行，就可以在执行耗时很长的任务时抽空执行一些快速的操作，比如在下载文件、解析文件的过程中更新 UI。
 
 **并行：** 并行代码指的是多段代码同时执行；比如一个拥有四核处理器的电脑可以同时运行四段代码，其中的每一个核都可以执行一个任务。
 
 > Swift 中的并发模型是基于线程的，但你不会直接和线程打交道。在 Swift 中，一个异步函数可以交出它在某个线程上的运行权，这样另一个异步函数在这个函数被阻塞时就能获得此线程的运行权。
 */

/*:
 ## 定义和调用异步函数
 
 异步函数或异步方法是一种能在运行中被挂起的特殊函数或方法。
 
 为了标记某个函数或者方法是异步的，你可以在它的声明中的参数列表后边加上 async 关键字。
 
 如果一个函数或方法有返回值，可以在返回箭头（->）前添加 async 关键字。
 
 对于那些既是异步又是 throwing 的函数，需要把 async 写在throws 关键字前边。
 
 调用一个异步方法时，执行会被挂起直到这个异步方法返回。你需要在调用前增加 await 关键字来标记此处为可能的悬点（Suspension point）。代码中被 await 标记的悬点表明当前这段代码可能会暂停等待异步方法或函数的返回。这也被称为让出线程（yielding the thread），因为在幕后 Swift 会挂起你这段代码在当前线程的执行，转而让其他代码在当前线程执行。
 
 > 学习并行的过程中，Task.sleep(_:) 方法非常有用。这个方法什么都没有做，只是等待不少于指定的时间（单位纳秒）后返回。下面是使用 sleep() 方法模拟网络请求实现 listPhotos(inGallery:) 的一个版本：
 */

/*:
```
func listPhotos(inGallery name: String) async -> [String] {
    await Task.sleep(2 * 1_000_000_000)  // 两秒
    return ["IMG001", "IMG99", "IMG0404"]
}

let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
 ```
*/

/*:
 ## 异步序列
 
 for-await-in 循环会在每次循环开始的时候因为有可能需要等待下一个元素而挂起当前代码的执行。
 如果想让自己创建的类型使用 for-await-in 循环，就需要遵循 AsyncSequence 协议。
 
 ```
 let handle = FileHandle.standardInput
 for try await line in handle.bytes.lines {
     print(line)
 }
 ```
 */

/*:
 ## 并行调用异步方法
 
 为了在调用异步函数的时候让它附近的代码并发执行，定义一个常量时，在 let 前添加 async 关键字，然后在每次使用这个常量时添加 await 标记。
 
 ```
 async let firstPhoto = downloadPhoto(named: photoNames[0])  // 这里用await是串行的！
 async let secondPhoto = downloadPhoto(named: photoNames[1]) // 这里用await是串行的！
 async let thirdPhoto = downloadPhoto(named: photoNames[2])  // 这里用await是串行的！

 let photos = await [firstPhoto, secondPhoto, thirdPhoto] // 三张图片同时下载，都完成后，执行下面的show
 show(photos)
 ```
 */

/*:
 ## 并行调用异步方法
 
 任务（task)是一项工作，可以作为程序的一部分并发执行。所有的异步代码都属于某个任务。上一部分介绍的 async-let 语法就会产生一个子任务。
 
 任务是按层级结构排列的。同一个任务组中的任务拥有相同的父任务，并且每个任务都可以添加子任务。由于任务和任务组之间明确的关系，这种方式又被称为结构化并发（structured concurrency）。
 
 ```
 await withTaskGroup(of: Data.self) { taskGroup in
     let photoNames = await listPhotos(inGallery: "Summer Vacation")
     for name in photoNames {
         taskGroup.async { await downloadPhoto(named: name) }
     }
 }
 ```
 
 ## 任务取消
 
 如果想检查任务是否被取消，既可以使用 Task.checkCancellation()（如果任务取消会返回 CancellationError），也可以使用 Task.isCancelled 来判断，继而在代码中对取消进行相应的处理。比如，一个从图库中下载图片的任务需要删除下载到一半的文件并且关闭连接。
 如果想手动执行扩散取消，调用 Task.cancel()。
 */

/*:
 ## Actors
 
 actor 在同一时间只允许一个任务访问它的可变状态，这使得多个任务中的代码与一个 actor 交互时更加安全。
 Swift 可以保证只有 actor 内部的代码可以访问 actor 的内部状态。这个保证也被称为 actor isolation。外部代码访问内部属性，需要使用await
 
 ```
 actor TemperatureLogger {
     let label: String
     var measurements: [Int]
     private(set) var max: Int

     init(label: String, measurement: Int) {
         self.label = label
         self.measurements = [measurement]
         self.max = measurement
     }
 }
 
 extension TemperatureLogger {
     // 内部方法正常访问max
     func update(with measurement: Int) {
         measurements.append(measurement)
         if measurement > max {
             max = measurement
         }
     }
 }
 
 //
 let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
 print(await logger.max)  // 外部代码访问max，需要用await，否则会报错
 // 输出 "25"
 ```
 */
